package com.example.securitydemo.util;

import com.example.securitydemo.config.AppProperties;
import com.example.securitydemo.domain.User;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;
import java.util.Optional;
import java.util.stream.Collectors;

@RequiredArgsConstructor
@Component
public class JwtUtil {

    // 用于签名 Access Token
    public static final Key accessTokenKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);
    // 用于签名 Refresh Token
    public static final Key refreshTokenKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);

    //
    private final AppProperties appProperties;

    /**
     * 放行令牌
     * @param userDetails
     * @return
     */
    public String createAccessToken(UserDetails userDetails) {
        return createJwtToken(userDetails, appProperties.getJwt().getAccessTokenExpireTime(), accessTokenKey);
    }

    public boolean validateAccessTokenWithoutExpired(String accessToken) {
        return validateAccessToken(accessToken);
    }

    public boolean validateAccessToken(String accessToken) {
        return validateToken(accessToken, JwtUtil.accessTokenKey, true);
    }

    public boolean validateRefreshToken(String refreshToken) {
        return validateToken(refreshToken, JwtUtil.refreshTokenKey, false);
    }

    public boolean validateToken(String token, Key key, boolean isExpiredValid) {
        try {
            Jwts.parserBuilder().setSigningKey(key)
                    .build()
                    .parseClaimsJws(token);
        } catch (ExpiredJwtException | SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {
            if (e instanceof ExpiredJwtException) {
                return isExpiredValid;
            }
            return false;
        }
        return true;
    }

    public String buildAccessTokenWithRefreshToken(String refreshToken) {
        return parseClaims(refreshToken).map(claims -> {
            return Jwts.builder().setClaims(claims)
                    .setExpiration(new Date(System.currentTimeMillis() +
                            appProperties.getJwt().getAccessTokenExpireTime()))
                    .signWith(accessTokenKey)
                    .compact();
        }).orElseThrow();
    }

    public Optional<Claims> parseClaims(String refreshToken) {
        try {
            return Optional.of(
                    Jwts.parserBuilder().setSigningKey(JwtUtil.refreshTokenKey).build()
                            .parseClaimsJws(refreshToken).getBody()
            );
        } catch (Exception e) {
            return Optional.empty();
        }
    }

    /**
     * 放行令牌
     * @param userDetails
     * @param expireTime
     * @return
     */
    public String createAccessToken(UserDetails userDetails, long expireTime) {
        return createJwtToken(userDetails, expireTime, accessTokenKey);
    }

    /**
     * 刷新令牌
     * @param userDetails
     * @return
     */
    public String createRefreshToken(UserDetails userDetails) {
        return createJwtToken(userDetails, appProperties.getJwt().getRefreshTokenExpireTime(), refreshTokenKey);
    }

    /**
     * 刷新令牌
     * @param userDetails
     * @param expireTime
     * @return
     */
    public String createRefreshToken(UserDetails userDetails, long expireTime) {
        return createJwtToken(userDetails, expireTime, refreshTokenKey);
    }

    public String createJWTToken(UserDetails userDetails, long timeToExpire) {
        return createJwtToken(userDetails, timeToExpire, accessTokenKey);
    }

    /**
     * 根据用户信息生成一个 JWT
     *
     * @param userDetails  用户信息
     * @param timeToExpire 毫秒单位的失效时间
     * @param signKey      签名使用的 key
     * @return JWT
     */
    public String createJwtToken(UserDetails userDetails, long timeToExpire, Key signKey) {
        long now = System.currentTimeMillis();
        return Jwts.builder()
                .setId("imooc")
                .setSubject(userDetails.getUsername())
                // claim 自定义字段
                .claim("authorities", // 权限列表
                        userDetails.getAuthorities().stream()
                            .map(GrantedAuthority::getAuthority)
                            .collect(Collectors.toList())
                )
                .setIssuedAt(new Date(now)) // 开始时间
                .setExpiration(new Date(now + timeToExpire)) // 过期时间
                .signWith(signKey)
                .compact();
    }

}
